// SPDX-FileCopyrightText: Copyright 2023 CitruS Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "binder.h"
#include "core/core.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/patch_manager.h"
#include "core/loader/loader.h"
#include "core/loader/nro.h"
#include "native.h"

using RomMetadata = aidl::com::fanwj::citrus::aidl::IBackend::RomMetadata;

std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache;

RomMetadata CacheRomMetadata(const std::string& path) {
    const auto file =
        Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
    auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);

    RomMetadata entry;
    loader->ReadTitle(entry.title);
    loader->ReadProgramId(reinterpret_cast<u64 &>(entry.programId));
    loader->ReadIcon(entry.icon);

    const FileSys::PatchManager pm{
        static_cast<u64>(entry.programId), EmulationSession::GetInstance().System().GetFileSystemController(),
        EmulationSession::GetInstance().System().GetContentProvider()};
    const auto control = pm.GetControlMetadata();

    if (control.first != nullptr) {
        entry.developer = control.first->GetDeveloperName();
        entry.version = control.first->GetVersionString();
    } else {
        FileSys::NACP nacp;
        if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
            entry.developer = nacp.GetDeveloperName();
        } else {
            entry.developer = "";
        }

        entry.version = "1.0.0";
    }

    if (loader->GetFileType() == Loader::FileType::NRO) {
        auto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
        entry.isHomebrew = loader_nro->IsHomebrew();
    } else {
        entry.isHomebrew = false;
    }

    m_rom_metadata_cache[path] = entry;

    return entry;
}

RomMetadata GetRomMetadata(const std::string& path, bool reload = false) {
    if (reload) {
        return CacheRomMetadata(path);
    }

    if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
        return search->second;
    }

    return CacheRomMetadata(path);
}

::ndk::ScopedAStatus Binder::GameMetadata_getMetadata(const std::string& path, bool reload, IBackend::RomMetadata* _aidl_return) {
    *_aidl_return = GetRomMetadata(path, reload);
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::GameMetadata_isValid(const std::string& path, bool* _aidl_return) {
    *_aidl_return = false;
    const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
        path, FileSys::OpenMode::Read);
    if (file) {
        auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file);
        if (loader) {
            const auto file_type = loader->GetFileType();
            if (file_type != Loader::FileType::Unknown && file_type != Loader::FileType::Error) {
                u64 program_id = 0;
                Loader::ResultStatus res = loader->ReadProgramId(program_id);
                if (res == Loader::ResultStatus::Success) {
                    *_aidl_return = true;
                }
            }
        }
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::GameMetadata_resetMetadata() {
    m_rom_metadata_cache.clear();
    return ::ndk::ScopedAStatus::ok();
}
